/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// Copyright (c) 1996-2014, Live Networks, Inc.  All rights reserved
// LIVE555 Proxy Server
// main program

#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <iostream>
#include <libxml/parser.h>
#include <string.h>
#include <stdlib.h>  
#include <errno.h>  
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;

#define MAXDATASIZE 4096
#define SERVERPORT 8087
#define MAXCONN_NUM 10

char const* progName;
UsageEnvironment* env;
UserAuthenticationDatabase* authDB = NULL;
UserAuthenticationDatabase* authDBForREGISTER = NULL;

// Default values of command-line parameters:
int verbosityLevel = 0;
Boolean streamRTPOverTCP = False;
portNumBits tunnelOverHTTPPortNum = 0;
char* username = NULL;
char* password = NULL;
Boolean proxyREGISTERRequests = False;
char* usernameForREGISTER = NULL;
char* passwordForREGISTER = NULL;
RTSPServer* rtspServer;
pthread_t lpt;
char *registeredcenter="";
char buf[ MAXDATASIZE]; 
int numbytes,new_fd,sockfd;
struct sockaddr_in server_addr; 
struct sockaddr_in client_addr; 
const char * registeredmsg = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
                            <Envelope type=\"r_pregister\"> \
                                success \
                            </Envelope>";


void *ListeningThread(void *arg);
int DecodeXml(char * buffer);

static RTSPServer* createRTSPServer(Port port) {
  if (proxyREGISTERRequests) {
    return RTSPServerWithREGISTERProxying::createNew(*env, port, authDB, authDBForREGISTER, 65, streamRTPOverTCP, verbosityLevel);
  } else {
    return RTSPServer::createNew(*env, port, authDB);
  }
}

void usage() {
  *env << "Usage: " << progName
       << " [-v|-V]"
       << " [-t|-T <http-port>]"
       << " [-u <username> <password>]"
       << " [-R] [-U <username-for-REGISTER> <password-for-REGISTER>]"
       << " <rtsp-url-1> ... <rtsp-url-n>\n";
  exit(1);
}

int main(int argc, char** argv) {
  // Increase the maximum size of video frames that we can 'proxy' without truncation.
  // (Such frames are unreasonably large; the back-end servers should really not be sending frames this large!)
  OutPacketBuffer::maxSize = 200000; // bytes

  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  env = BasicUsageEnvironment::createNew(*scheduler);

  *env << "LIVE555 Proxy Server\n"
       << "\t(LIVE555 Streaming Media library version "
       << LIVEMEDIA_LIBRARY_VERSION_STRING << ")\n\n";

  // Check command-line arguments: optional parameters, then one or more rtsp:// URLs (of streams to be proxied):
  progName = argv[0];
  if (argc < 2) usage();
  while (argc > 1) {
    // Process initial command-line options (beginning with "-"):
    char* const opt = argv[1];
    if (opt[0] != '-') break; // the remaining parameters are assumed to be "rtsp://" URLs

    switch (opt[1]) {
    case 'v': { // verbose output
      verbosityLevel = 1;
      break;
    }

    case 'V': { // more verbose output
      verbosityLevel = 2;
      break;
    }

    case 't': {
      // Stream RTP and RTCP over the TCP 'control' connection.
      // (This is for the 'back end' (i.e., proxied) stream only.)
      streamRTPOverTCP = True;
      break;
    }

    case 'T': {
      // stream RTP and RTCP over a HTTP connection
      if (argc > 3 && argv[2][0] != '-') {
	// The next argument is the HTTP server port number:                                                                       
	if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1
	    && tunnelOverHTTPPortNum > 0) {
	  ++argv; --argc;
	  break;
	}
      }

      // If we get here, the option was specified incorrectly:
      usage();
      break;
    }

    case 'u': { // specify a username and password (to be used if the 'back end' (i.e., proxied) stream requires authentication)
      if (argc < 4) usage(); // there's no argv[3] (for the "password")
      username = argv[2];
      password = argv[3];
      argv += 2; argc -= 2;
      break;
    }

    case 'U': { // specify a username and password to use to authenticate incoming "REGISTER" commands
      if (argc < 4) usage(); // there's no argv[3] (for the "password")
      usernameForREGISTER = argv[2];
      passwordForREGISTER = argv[3];

      if (authDBForREGISTER == NULL) authDBForREGISTER = new UserAuthenticationDatabase;
      authDBForREGISTER->addUserRecord(usernameForREGISTER, passwordForREGISTER);
      argv += 2; argc -= 2;
      break;
    }

    case 'R': { // Handle incoming "REGISTER" requests by proxying the specified stream:
      proxyREGISTERRequests = True;
      break;
    }

    default: {
      usage();
      break;
    }
    }

    ++argv; --argc;
  }
  if (argc < 2 && !proxyREGISTERRequests) usage(); // there must be at least one "rtsp://" URL at the end 
  // Make sure that the remaining arguments appear to be "rtsp://" URLs:
  int i;
  for (i = 1; i < argc; ++i) {
    if (strncmp(argv[i], "rtsp://", 7) != 0) usage();
  }
  // Do some additional checking for invalid command-line argument combinations:
  if (authDBForREGISTER != NULL && !proxyREGISTERRequests) {
    *env << "The '-U <username> <password>' option can be used only with -R\n";
    usage();
  }
  if (streamRTPOverTCP) {
    if (tunnelOverHTTPPortNum > 0) {
      *env << "The -t and -T options cannot both be used!\n";
      usage();
    } else {
      tunnelOverHTTPPortNum = (portNumBits)(~0); // hack to tell "ProxyServerMediaSession" to stream over TCP, but not using HTTP
    }
  }

#ifdef ACCESS_CONTROL
  // To implement client access control to the RTSP server, do the following:
  authDB = new UserAuthenticationDatabase;
  authDB->addUserRecord("username1", "password1"); // replace these with real strings
      // Repeat this line with each <username>, <password> that you wish to allow access to the server.
#endif

  // Create the RTSP server.  Try first with the default port number (554),
  // and then with the alternative port number (8554):

  portNumBits rtspServerPortNum = 554;
  rtspServer = createRTSPServer(rtspServerPortNum);
  if (rtspServer == NULL) {
    rtspServerPortNum = 8554;
    rtspServer = createRTSPServer(rtspServerPortNum);
  }
  if (rtspServer == NULL) {
    *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }

  // Create a proxy for each "rtsp://" URL specified on the command line:
  for (i = 1; i < argc; ++i) {
    char const* proxiedStreamURL = argv[i];
    char streamName[30];
    if (argc == 2) {
      sprintf(streamName, "%s", "proxyStream"); // there's just one stream; give it this name
    } else {
      sprintf(streamName, "proxyStream-%d", i); // there's more than one stream; distinguish them by name
    }
    ServerMediaSession* sms
      = ProxyServerMediaSession::createNew(*env, rtspServer,
					   proxiedStreamURL, streamName,
					   username, password, tunnelOverHTTPPortNum, verbosityLevel);
    rtspServer->addServerMediaSession(sms);

    char* proxyStreamURL = rtspServer->rtspURL(sms);
    *env << "RTSP stream, proxying the stream \"" << proxiedStreamURL << "\"\n";
    *env << "\tPlay this stream using the URL: " << proxyStreamURL << "\n";
    delete[] proxyStreamURL;
  }

  if (proxyREGISTERRequests) {
    *env << "(We handle incoming \"REGISTER\" requests on port " << rtspServerPortNum << ")\n";
  }

  // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
  // Try first with the default HTTP port (80), and then with the alternative HTTP
  // port numbers (8000 and 8080).

  if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
    *env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n";
  } else {
    *env << "\n(RTSP-over-HTTP tunneling is not available.)\n";
  }
  int lthread = pthread_create(&lpt, NULL, ListeningThread, NULL);
  if(lthread < 0){
     perror("Listen thread create failed!");
  }
  // Now, enter the event loop:
  env->taskScheduler().doEventLoop(); // does not return

  return 0; // only to prevent compiler warning
}

char * CreateNewSMS(char const* oldrtsp,const char * streamName)
{
	ServerMediaSession* sms = ProxyServerMediaSession::createNew(*env, rtspServer,oldrtsp, streamName,NULL, NULL, 0, 0);
	if (sms != NULL){
		rtspServer->addServerMediaSession(sms);
		*env << "=================================================================\n";
		*env << "Proxying the stream \"" << oldrtsp << "\"\n";
		*env << "Play this stream using the URL: " << rtspServer->rtspURL(sms) << "\n";
		return rtspServer->rtspURL(sms) ;
	}
	else{
		*env << "Proxying the stream \"" << oldrtsp << "\",create failed!\n";
		return NULL;
	}
}


void *ListeningThread(void *arg)
{
    socklen_t sin_size; 
    if ((sockfd = socket ( AF_INET , SOCK_STREAM , 0)) == - 1) { 
        perror ("socket error"); 
        pthread_exit((void *)-1);
    } 
    memset(&client_addr,0,sizeof(struct sockaddr)); 
    server_addr.sin_family = AF_INET; 
    server_addr.sin_port = htons(SERVERPORT); 
    server_addr.sin_addr.s_addr = INADDR_ANY; 
    if ( bind ( sockfd, (struct sockaddr *) & server_addr, sizeof (struct sockaddr)) == -1) {
        perror ("bind error"); 
        pthread_exit((void *)-1);
    } 
    if (listen(sockfd, MAXCONN_NUM) ==-1) { 
        perror("listen error"); 
        pthread_exit((void *)-1);
    } 

    while (1) {
        sin_size = sizeof ( struct sockaddr_in ); 
        if ( ( new_fd = accept ( sockfd, (struct sockaddr *)&client_addr, &sin_size) ) == - 1) { 
            perror ( "accept error" ); 
            continue ; 
        } 
        printf ( "proxyserver: got connection from %s\n" , inet_ntoa( client_addr. sin_addr) ); 
        while (1) {
            if ( ( numbytes = recv ( new_fd, buf, MAXDATASIZE, 0) ) == - 1) {
                perror ( "recv error" );
                pthread_exit((void *)-1);
            }
            buf[numbytes] = '\0';
            if(strlen(buf) <= 0){
                break;
            }
           // printf("received:%s\n",buf);
            DecodeXml(buf);

        }
        printf("Server: disconnect from %s\n",inet_ntoa( client_addr. sin_addr) );
        close ( new_fd); 
    } 
}

char * replaceAll(char * src,char oldChar,char add){
  char * head=src;
  while(*src!='\0'){
    if(*src==oldChar){
      char * tmp = src;
      while(*tmp!='\0'){
        *tmp = *(tmp+1);
        tmp++;
      }
    }
    src++;
  }
  *src=add;
  *(++src)='\0';
  return head;
}


int DecodeXml(char * buffer){
    xmlDocPtr doc = xmlParseMemory(buffer,strlen(buffer));
    if (doc == NULL){
        return -1;
    }
    xmlNodePtr curNode = xmlDocGetRootElement(doc); //get root element
    if (curNode == NULL){
        xmlFreeDoc(doc);
        return -2;
    }
    if (xmlStrcmp(curNode->name, BAD_CAST "Envelope")){  //匹配Envelope
        xmlFreeDoc(doc);
        return -3;
    }
    if (xmlHasProp(curNode,BAD_CAST "type")){
        xmlChar * szAttr = xmlGetProp(curNode,BAD_CAST "type");
        cout <<"XMLDecoded:" << szAttr << endl;

        if(!xmlStrcmp(szAttr,BAD_CAST "pregister")){  //匹配cmsregister
            registeredcenter = inet_ntoa( client_addr. sin_addr);
            if ( send ( new_fd, registeredmsg , strlen(registeredmsg), 0) == - 1) { 
                perror ( "send error" ); 
            } 
            cout << "proxyserver got a centerserver registered from " <<registeredcenter<<endl;
        }

        if(strcmp(registeredcenter,inet_ntoa(client_addr.sin_addr))){  
            cout << "you has not registered! please register first...." <<endl;
        }else{

            if(!xmlStrcmp(szAttr,BAD_CAST "getrtspuri")){  //匹配getrtspuri
                xmlNodePtr sNode = curNode->xmlChildrenNode;
                int count = 1;
                while (sNode != NULL){
                    if(!xmlStrcmp(sNode->name,BAD_CAST "profile")){
                        xmlNodePtr cNode = sNode->xmlChildrenNode;
                        cout << sNode->name <<count <<endl;
                        while(cNode != NULL){
                            char * deviceip;
                            if (!xmlStrcmp(cNode->name,BAD_CAST "deviceip")){
                                deviceip = replaceAll((char *)xmlNodeGetContent(cNode),'.',(char)(count+48));
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            if(!xmlStrcmp(cNode->name,BAD_CAST "token")){
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            if(!xmlStrcmp(cNode->name,BAD_CAST "sourceuri")){
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                                char *desturi = CreateNewSMS((char *)xmlNodeGetContent(cNode),deviceip);
                                if(desturi != NULL){
                                   xmlNewTextChild(sNode, NULL, BAD_CAST "desturi", BAD_CAST desturi);
                                }
                            }
                            cNode = cNode->next;
                        }
                        xmlFree(cNode);
                        count++;
                    }
                    sNode = sNode->next;
                }
                xmlFree(sNode);

                xmlChar *xml_buff;
                int size;
                xmlDocDumpMemory(doc,&xml_buff,&size);
                //printf("%s-----%d\n",(char *)xml_buff,size);
                if (send ( new_fd, (char*)xml_buff , strlen((char*)xml_buff), 0) == - 1) { 
                    perror ( "send error" ); 
                } 
            }
        }
        xmlFree(szAttr);
    }
    xmlFreeDoc(doc);

}

